package com.vinoxm.gateway.filter;

import org.reactivestreams.Publisher;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URI;

/*
 *  问题: oauth2.0通过Gateway请求授权码不能回调到return_uri
 *  原因: SpringSecurity会缓存登录前的上一次请求在session中,在登录成功后,跳往该请求
 *  解决: 在Gateway添加过滤器,当重定向到认证服务的登录页时，将认证服务的域名和端口替换成网关的域名和端口
 */
@Component
public class Oauth2ResponseFilter implements GlobalFilter, Ordered {

    /*认证服务器路由地址*/
    @Value("${config.auth-path}")
    private String authPath;

    /*请求授权码时的URI*/
    private static String savedUri;

    private static final String AUTH_AUTHORIZE = "/oauth/authorize";

    private static final String LOGIN = "/login";

    @Override
    public int getOrder() {
        return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String path = exchange.getRequest().getPath().value();
        if (path.contains(AUTH_AUTHORIZE) || path.contains(LOGIN) ) {
            if (path.contains(AUTH_AUTHORIZE)) {
                URI uri = exchange.getRequest().getURI();
                savedUri = uri.getScheme() + "://" + uri.getAuthority();
            }
            //构建响应包装类
            return chain.filter(exchange.mutate().response(new ServerHttpResponseDecorator(exchange.getResponse()) {
                @Override
                public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                    HttpStatus status = this.getStatusCode();
                    if (HttpStatus.FOUND.equals(status)) {
                        String location = getHeaders().getFirst("Location");
                        assert location != null;
                        String replaceLocation = location.replaceAll("^((ht|f)tps?):\\/\\/(\\d{1,3}.){3}\\d{1,3}(:\\d+)?", getCrossPath());
                        if (location.contains("code=")) {

                        } else {
                            getHeaders().set("Location", replaceLocation);
                        }
                    }
                    this.getStatusCode();
                    return super.writeWith(body);
                }
            }).build());
        }
        return chain.filter(exchange);
    }

    private String getCrossPath() {
        return savedUri + authPath;
    }
}